package org.example.test03;


import com.intellij.openapi.project.Project;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;

import javax.swing.*;
import java.security.ProtectionDomain;

/**
 * 通过 ByteBuddy + Attach API 对当前 IDE 进程做动态增强。
 * 编译增强代码时使用 Janino， interceptor 源码里全部用全限定名，避免 Janino 找不到类。
 */
public class AgentRunner {

    private final Project project;

    public AgentRunner(Project project) {
        this.project = project;
    }

    public void run(String classPattern, String methodPattern, String body) {
        System.out.println(">>> ByteBuddyPanel.run()");
        try {
            /* 1. 拿到Instrumentation */
            var inst = ByteBuddyAgent.install();

            /* 2. 拼出要动态编译的拦截器源码 —— 全部用全限定名 */
            String interceptorSrc = """
                    package com.example.bytebuddy.generated;
                    public class Interceptor {
                        public static Object intercept(
                                @net.bytebuddy.implementation.bind.annotation.Origin java.lang.reflect.Method method,
                                @net.bytebuddy.implementation.bind.annotation.This java.lang.Object obj,
                                @net.bytebuddy.implementation.bind.annotation.AllArguments java.lang.Object[] args)
                                throws java.lang.Exception {
                            %s
                        }
                    }
                    """.formatted(body);

            /* 3. 使用Janino在内存编译 */
            org.codehaus.janino.SimpleCompiler compiler = new org.codehaus.janino.SimpleCompiler();
            compiler.cook(new java.io.StringReader(interceptorSrc));
            Class<?> interceptorClass = compiler.getClassLoader()
                    .loadClass("com.example.bytebuddy.generated.Interceptor");

            /* 4. ByteBuddy 动态 redefine */
            System.out.println(">>> 即将 installOn");
            Class<?> finalInterceptorClass = interceptorClass;   // 事实最终变量
            new AgentBuilder.Default()
                    .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
                    .ignore(ElementMatchers.nameStartsWith("net.bytebuddy.")
                            .or(ElementMatchers.nameStartsWith("com.intellij.")))
                    .type(ElementMatchers.nameMatches(classPattern))
                    .transform(new AgentBuilder.Transformer() {
                        @Override
                        public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder,
                                                                net.bytebuddy.description.type.TypeDescription typeDesc,
                                                                @net.bytebuddy.utility.nullability.MaybeNull ClassLoader classLoader,
                                                                @net.bytebuddy.utility.nullability.MaybeNull JavaModule module,
                                                                @net.bytebuddy.utility.nullability.MaybeNull ProtectionDomain protectionDomain) {
                            System.out.println(">>> ByteBuddy 尝试匹配类：" + typeDesc.getName());
                            return builder.method(ElementMatchers.nameMatches(methodPattern))
                                    .intercept(MethodDelegation.to(finalInterceptorClass));
                        }
                    })
                    .installOn(inst);
            System.out.println(">>> 已完成 installOn");

            SwingUtilities.invokeLater(() ->
                    javax.swing.JOptionPane.showMessageDialog(
                            null, "Attach & Transform 成功！", "Done", javax.swing.JOptionPane.INFORMATION_MESSAGE));
        } catch (Exception ex) {
            SwingUtilities.invokeLater(() ->
                    javax.swing.JOptionPane.showMessageDialog(
                            null, "失败: " + ex.getMessage(), "Error", javax.swing.JOptionPane.ERROR_MESSAGE));
        }
    }
}